#!/usr/bin/perl
use strict;

my %headers = (
	"video" => "videodev2.h",
	"subdev" => "v4l2-subdev.h",
	"frontend" => "dvb/frontend.h",
	"demux" => "dvb/dmx.h",
);

my %structs;
my %ioc;
my $size_ioc = 0;
my $size_struct = 0;

sub print_union()
{
	printf "union v4l_parms {\n";
	printf "\tint		i;\n";
	printf "\tunsigned long	ulong;\n";
	printf "\tu_int32_t	u32;\n";
	printf "\tv4l2_std_id	id;\n";
	printf "\tenum v4l2_priority prio;\n";
	printf "\n\t/* ioctl structs */\n";
	foreach my $s (sort keys %structs) {
		next if (!($s =~ m/struct/));
		$s =~ s/struct\s*//;

		my $ntabs = ($size_struct - length($s) - 7) / 8;
		my $tab = "";
		for (my $i = 0; $i < $ntabs; $i++) {
			$tab .= "\t";
		}


		printf "\tstruct %s%sp_%s;\n", $s, $tab, $s;
	}
	printf "};\n";
}

sub print_ioc()
{
	printf "#define ioc(type, cmd) { CMD32_##cmd, CMD64_##cmd, cmd, #type, #cmd }\n";
	printf "\n/* All defined ioctls */\n";
	printf "static const struct {\n";
	printf "\tu_int32_t cmd32;\t/* The 32-bit ioctl value, should never change */\n";
	printf "\tu_int32_t cmd64;\t/* The 64-bit ioctl value, should never change */\n";
	printf "\tu_int32_t cmd;\n";
	printf "\tconst char *type;\n";
	printf "\tconst char *name;\n";
	printf "} ioctls[] = {\n";
	printf "\t/* ioctl structs */\n";
	foreach my $ioctl (sort keys %ioc) {
		my $struct = $ioc{$ioctl};
		$struct =~ s/.*\://;

		my $type = $ioc{$ioctl};
		$type =~ s/\:.*//;

		my $ntabs = ($size_ioc - 8 - length($ioctl) - length($type)) / 8;
		my $tab = "";
		for (my $i = 0; $i < $ntabs; $i++) {
			$tab .= "\t";
		}

		printf "\tioc(%s, %s),%s/* %s */\n", $type, $ioctl, $tab, $struct;
	}
	printf "};\n";
	printf "#define S_IOCTLS sizeof(ioctls)/sizeof(ioctls[0])\n";
}

printf "/* This file is auto-generated by make sync-with-kernel */\n";
printf "#include <linux/compiler.h>\n";

foreach my $h (sort keys %headers) {
	my $line;

	printf "#include \"linux/%s\"\n", $headers{$h};

	open IN, "../../include/linux/" . $headers{$h};
	while (<IN>) {
		$line .= $_;
		if (/\\\s*$/) {
			$line =~ s/\\\s*$//;
			next;
		}
		if ($line =~ m/\#define\s+([A-Z][A-Z0-9_]+)\s+\_IO.+\(.*\,\s*([^\)]+)\)/) {
			$structs{$2} = 1;
			$ioc{$1} = "$h: $2";
			if ($size_ioc < length($1)) {
				$size_ioc = length($1);
			}
			if ($size_struct < length($2)) {
				$size_struct = length($2);
			}
		}
		if ($line =~ m/\#define\s+([A-Z][A-Z0-9_]+)\s+\_IO\(.*\)/) {
			$ioc{$1} = "$h: void";
			if ($size_ioc < length($1)) {
				$size_ioc = length($1);
			}
		}
		$line = "";
	}
	$size_ioc = int((9 + $size_ioc + 7) / 8) * 8;
	$size_struct = int(($size_struct + 7) / 8) * 8;
}

my $arg = shift;
if ($arg eq "--gen_ioctl_numbers") {
	printf "#include <stdio.h>\n";
	printf "int main(int argc, char *argv[]) {\n";
	printf "\tprintf(\"/* This file is auto-generated by make sync-with-kernel */\\n\\n\");\n";
	foreach my $ioctl (sort keys %ioc) {
		printf "\tprintf(\"#define CMD%%s_%s 0x%%x\\n\", argv[1], %s);\n", $ioctl, $ioctl;
	}
	printf "return 0;\n";
	printf "}\n";
} else {
	printf "#include \"ioctl_32.h\"\n";
	printf "#include \"ioctl_64.h\"\n\n";

	print_union();
	print_ioc();
}
